/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-08-23 16:24:23
 * @LastEditTime: 2021-09-08 10:37:50
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "ft_assert.h"
#include "interrupt.h"
#include "gmac.h"
#include "gmac_hw.h"

#define GMAC_CALL_EVT_HANDLER(pCtrl, evt)    \
    {\
        if (NULL != (pCtrl)->evtHandler[(evt)]) \
        {                                        \
            (pCtrl)->evtHandler[evt]((void *)(pCtrl));  \
        }\
    }

/**
 * @name: GmacGetIntrMask
 * @msg: get intrrupt mask
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u32} intrType
 */
u32 GmacGetIntrMask(GmacCtrl *pCtrl, u32 intrType)
{
    FT_ASSERTZERONUM(pCtrl && (GMAC_MAX_INTR_TYPE > intrType));
    u32 mask = 0;

    if (GMAC_CTRL_INTR == intrType)
    {
        mask = GMAC_READ_REG(pCtrl, GMAC_INTR_MASK_OFFSET);
    }
    else
    {
        mask = GMAC_READ_REG(pCtrl, GMAC_DMA_INTR_OFFSET);
    }

    return mask;
}

/**
 * @name: GmacSetIntrMask
 * @msg: set intrrupt mask
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u32} intrType
 * @param {u32} mask
 * @param {boolean} enable
 */
void GmacSetIntrMask(GmacCtrl *pCtrl, u32 intrType, u32 mask, boolean enable)
{
    FT_ASSERTVOID(pCtrl && (GMAC_MAX_INTR_TYPE > intrType));
    u32 regVal = GmacGetIntrMask(pCtrl, intrType);

    if (GMAC_CTRL_INTR == intrType)
    {
        /* 置 1 时, 禁止该位对应的中断信号 */
        if (TRUE == enable)
            regVal &= (~mask);
        else
            regVal |= mask;

        GMAC_WRITE_REG(pCtrl, GMAC_INTR_MASK_OFFSET, regVal);
    }
    else
    {
        /* 置 1 时, 使能该位对应的中断信号 */
        if (TRUE == enable)
            regVal |= mask;
        else
            regVal &= (~mask);

        GMAC_WRITE_REG(pCtrl, GMAC_DMA_INTR_OFFSET, regVal);
    }

    return;
}

/**
 * @name: GmacRegisterEvtHandler
 * @msg: register event handler
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u32} evt
 * @param {GmacEvtHandler} handler
 */
void GmacRegisterEvtHandler(GmacCtrl *pCtrl, u32 evt, GmacEvtHandler handler)
{
    FT_ASSERTVOID((NULL != pCtrl) && (GMAC_INTR_EVT_NUM > evt));
    pCtrl->evtHandler[evt] = handler;
}

/**
 * @name: GmacPhyStatusChecker
 * @msg: check physical status
 * @return {*}
 * @param {void} *param
 */
void GmacPhyStatusChecker(void *param)
{
    FT_ASSERTVOID(param != NULL);
    GmacCtrl *pCtrl = (GmacCtrl *)param;

    u32 phyStatus = GMAC_READ_REG(pCtrl, GMAC_MAC_PHY_STATUS);

    if (GMAC_RGSMIIIS_LNKSTS_UP == (GMAC_RGSMIIIS_LNKSTS & phyStatus))
    {
        GMAC_INFO("link is up");
    }
    else
    {
        GMAC_INFO("link is down");
    }

    return;
}

/**
 * @name: GmacDmaErrChecker
 * @msg: check and print dma error
 * @return {*}
 * @param {void} *param
 */
void GmacDmaErrChecker(void *param)
{
    FT_ASSERTVOID(param != NULL);
    GmacCtrl *pCtrl = (GmacCtrl *)param;
    u32 regVal = GMAC_READ_REG(pCtrl, GMAC_DMA_INTR_OFFSET);
    u32 status = GMAC_READ_REG(pCtrl, GMAC_DMA_STATUS_OFFSET);

    if ((GMAC_DMA_STATUS_TPS & status) && (GMAC_DMA_INTR_ENA_TSE & regVal))
    {
        GMAC_ERROR("Transmit process stopped");
    }

    if ((GMAC_DMA_STATUS_TU & status) && (GMAC_DMA_INTR_ENA_TUE & regVal))
    {
        GMAC_ERROR("Transmit Buffer Unavailable");
    }

    if ((GMAC_DMA_STATUS_TJT & status) && (GMAC_DMA_INTR_ENA_THE & regVal))
    {
        GMAC_ERROR("Transmit Jabber Timeout");
    }

    if ((GMAC_DMA_STATUS_OVF & status) && (GMAC_DMA_INTR_ENA_OVE & regVal))
    {
        GMAC_ERROR("Receive Overflow");
    }

    if ((GMAC_DMA_STATUS_UNF & status) && (GMAC_DMA_INTR_ENA_UNE & regVal))
    {
        GMAC_ERROR("Transmit Underflow");
    }

    if ((GMAC_DMA_STATUS_RU & status) && (GMAC_DMA_INTR_ENA_RUE & regVal))
    {
        GMAC_ERROR("Receive Buffer Unavailable");
    }

    if ((GMAC_DMA_STATUS_RPS & status) && (GMAC_DMA_INTR_ENA_RSE & regVal))
    {
        GMAC_ERROR("Receive Process Stopped");
    }

    if ((GMAC_DMA_STATUS_RWT & status) && (GMAC_DMA_INTR_ENA_RWE & regVal))
    {
        GMAC_ERROR("Receive Watchdog Timeout");
    }

    if ((GMAC_DMA_STATUS_ETI & status) && (GMAC_DMA_INTR_ENA_ETE & regVal))
    {
        GMAC_ERROR("Early Transmit Interrupt");
    }

    if ((GMAC_DMA_STATUS_FBI & status) && (GMAC_DMA_INTR_ENA_FBE & regVal))
    {
        GMAC_ERROR("Fatal Bus Error");
    }

    return;
}

/**
 * @name: GmacUpateLink
 * @msg: update gmac link status
 * @return {*}
 * @param {void} *para
 */
static void GmacUpateLink(void *para)
{
    FT_ASSERTVOID(para);
    GmacCtrl *pCtrl = (GmacCtrl *)para;
    u32 status = GMAC_READ_REG(pCtrl, GMAC_MAC_PHY_STATUS);
    u32 speedStatus, duplexStatus;
    u32 speed, duplex;

    /* Check the link status */
    if (GMAC_RGSMIIIS_LNKSTS_UP == (GMAC_RGSMIIIS_LNKSTS & status))
    {
        speedStatus = GMAC_RGSMIIIS_SPEED & status;
        duplexStatus = GMAC_RGSMIIIS_LNKMODE & status;

        if (GMAC_RGSMIIIS_SPEED_125MHZ == speedStatus)
            speed = GMAC_SPEED_1000;
        else if (GMAC_RGSMIIIS_SPEED_25MHZ == speedStatus)
            speed = GMAC_SPEED_100;
        else
            speed = GMAC_SPEED_10;

        if (GMAC_RGSMIIIS_LNKMODE_HALF == duplexStatus)
            duplex = GMAC_MODE_HALFDUPLEX;
        else
            duplex = GMAC_MODE_FULLDUPLEX;

        GMAC_INFO("link is up --- %d/%s", 
                   speed, (GMAC_MODE_FULLDUPLEX == duplex) ? "full" : "half");
    }
    else
    {
        GMAC_INFO("link is down");
    }

    (void)speed;
    (void)duplex;
}

/**
 * @name: GmacIntrHandler
 * @msg: handle gmac interrupt
 * @return {*}
 * @param {s32} vector
 * @param {void} *param
 */
static void GmacIntrHandler(s32 vector, void *param)
{
    FT_ASSERTVOID(param);
    (void)vector;
    GmacCtrl *pCtrl = (GmacCtrl *)param;

    /************dma intrrupt**************/
    if (GMAC_DMA_STATUS_GLI & GMAC_READ_REG(pCtrl, GMAC_DMA_STATUS_OFFSET))
    {
        GMAC_CALL_EVT_HANDLER(pCtrl, GMAC_PHY_STATUS_EVT);
    }

    if (GMAC_DMA_STATUS_RI & GMAC_READ_REG(pCtrl, GMAC_DMA_STATUS_OFFSET))
    {
        GMAC_CALL_EVT_HANDLER(pCtrl, GMAC_RX_COMPLETE_EVT);
        GMAC_W1C_BIT(pCtrl, GMAC_DMA_STATUS_OFFSET, GMAC_DMA_STATUS_RI);
    }

    if (GMAC_DMA_STATUS_TI & GMAC_READ_REG(pCtrl, GMAC_DMA_STATUS_OFFSET))
    {
        GMAC_CALL_EVT_HANDLER(pCtrl, GMAC_TX_COMPLETE_EVT);
        GMAC_W1C_BIT(pCtrl, GMAC_DMA_STATUS_OFFSET, GMAC_DMA_STATUS_TI);
    }

    GMAC_W1C_BIT(pCtrl, GMAC_DMA_STATUS_OFFSET, GMAC_DMA_STATUS_NIS);

    if (GMAC_DMA_STATUS_AIS & GMAC_READ_REG(pCtrl, GMAC_DMA_STATUS_OFFSET))
    {
        GMAC_ERROR("dma error");
        GMAC_CALL_EVT_HANDLER(pCtrl, GMAC_DMA_ERR_EVT);
        GMAC_W1C_BIT(pCtrl, GMAC_DMA_STATUS_OFFSET, GMAC_DMA_STATUS_CLR_ABLE);
    }

    /************gmac intrrupt**************/
    if (GMAC_ISR_STATUS_RSIS & GMAC_READ_REG(pCtrl, GMAC_INTR_STATUS_OFFSET))
    {
        GMAC_CALL_EVT_HANDLER(pCtrl, GMAC_LINK_STATUS_EVT);
    }

    return;     
}

/**
 * @name: GmacIntrInit
 * @msg: init and mask gmac intrrupt
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 */
u32 GmacIntrInit(GmacCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);

    GmacConfig *pConfig = &pCtrl->config;

    /* disable all gmac ctrl intr */
    GmacSetIntrMask(pCtrl, GMAC_CTRL_INTR, GENMASK(31, 0), FALSE);
    GmacSetIntrMask(pCtrl, GMAC_DMA_INTR, GENMASK(31, 0), FALSE);

    /* cpu EL1 非安全模式下，设置优先级和运行优先级是非线性的 */
    InterruptSetPriority(pConfig->irqNum, ((0x8 << 4) + 4 * 16));
    InterruptInstall(pConfig->irqNum, GmacIntrHandler, pCtrl, pConfig->irqName);
    
    /* register intr callback */
    GmacRegisterEvtHandler(pCtrl, GMAC_PHY_STATUS_EVT, GmacPhyStatusChecker);
    GmacRegisterEvtHandler(pCtrl, GMAC_DMA_ERR_EVT, GmacDmaErrChecker);
    GmacRegisterEvtHandler(pCtrl, GMAC_LINK_STATUS_EVT, GmacUpateLink);

    /* umask intr */
    if (pConfig->enableIrq)
        InterruptUmask(pConfig->irqNum);
    else
        InterruptMask(pConfig->irqNum);

    /* enable some gmac intr */
    GmacSetIntrMask(pCtrl, GMAC_CTRL_INTR, 
                    GMAC_ISR_MASK_RSIM ,TRUE);

    /* enable some dma intr */
    GmacSetIntrMask(pCtrl, GMAC_DMA_INTR, 
                    GMAC_DMA_INTR_ENA_NIE |
                    GMAC_DMA_INTR_ENA_RIE /*| GMAC_DMA_INTR_ENA_TIE*/, TRUE);

    GMAC_INFO("+++ gmac intrrupt setup");
    return GMAC_SUCCESS;
}